#include "LecteurPhraseAvecArbre.h"
#include "Exception.h"

#include <stdlib.h>
#include <iostream>
using namespace std;

//------------------------------- Constructeur ---------------------------------

   LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
	ls(nomFich), ts() {
}

//------------------------------------------------------------------------------

   void LecteurPhraseAvecArbre::analyse() {
        arbre=programme();
	cout << "Syntaxe correcte." << endl;
}

//////////////////////// IMPLEMENTATION DE LA GRAMMAIRE \\\\\\\\\\\\\\\\\\\\\\\\

 Noeud* LecteurPhraseAvecArbre::programme() {    // <programme> ::= debut <seq_inst> fin FIN_FICHIER

	sauterSymCour("debut");
	Noeud* si = seqInst();
	sauterSymCour("fin");
	testerSymCour("<FINDEFICHIER>");
	return si;
}

//------------------------------------------------------------------------------

 Noeud* LecteurPhraseAvecArbre::seqInst() {	// <seqInst> ::= <inst> ; { <inst> ; }
    
     NoeudSeqInst* si = new NoeudSeqInst();
	do {
                try
                {
                        si->ajouteInstruction(inst());
                        sauterSymCour(";");
                }
                catch (ErreurSyn e)       
                {
                    cout<<e.what()<<endl;
                    while (ls.getSymCour() != ";" && ls.getSymCour() != '<FINDEFICHER>')
                           ls.suivant;
                }
                
                
	} while (ls.getSymCour()=="<VARIABLE>"  ||
                 ls.getSymCour()=="si"          ||
                 ls.getSymCour()=="tantque"     ||
                 ls.getSymCour()=="repeter"     ||
                 ls.getSymCour()=="lire"        ||
                 ls.getSymCour()=="ecrire"      ||
                 ls.getSymCour()=="pour"        ||
                 ls.getSymCour()=="switch");      // tant que le symbole courant est un debut possible d'instruction...
	return si;
}

//------------------------------------------------------------------------------
 
Noeud* LecteurPhraseAvecArbre::inst() {         // <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <instLire> | <instEcrire> | <instPour>| <instSwitch>
    
        if (ls.getSymCour()==("<VARIABLE>"))
             return affectation();                              // appel à la méthode affectation()
        else if (ls.getSymCour()==("si"))
            return instSi();
        else if (ls.getSymCour()==("tantque"))
            return instTq();
        else if (ls.getSymCour()==("repeter"))
            return instRepeter();
        else if (ls.getSymCour()==("lire"))             
            return instLire();                                 // appel à la méthode instLire permettant de lire une variable (type <VARIABLE>) au clavier
        else if (ls.getSymCour()==("ecrire"))           
            return instEcrire();                               // appel à la méthode instEcrire, permettant l'affichage à l'ecran de variables, entiers, chaines.
        else if (ls.getSymCour()==("pour"))           
            return instPour(); 
        else if (ls.getSymCour()=="switch")
            return instSwitch();
        else
            erreur("<inst>");
        return NULL;
}

        //--------------------------------- SI ---------------------------------

Noeud* LecteurPhraseAvecArbre::instSi() {      // <instSi> ::= si ( <expBoolOU> ) <seqInst> { sinonsi ( <expBoolOU> ) <seqInst> } [ sinon <seqInst> ] finsi
      
       Noeud* suivant = NULL;                   // déclaration d'un pointeur *suivant qui pointe sur un Noeud initialisé à NULL
       Noeud* exp = NULL;                       // déclaration d'un pointeur *exp qui pointe sur un Noeud initialisé à NULL
       Noeud* seqinst = NULL;                   // déclaration d'un pointeur *seqinst qui pointe sur un Noeud initialisé à NULL
       Noeud* si = NULL;                        // déclaration d'un pointeur *si qui pointe sur un Noeud initialisé à NULL
       
       if (ls.getSymCour()=="si") {            // si le symbole courant est un si

          sauterSymCour("si");                  // on saute le symbole si
          sauterSymCour("(");                   // on saute (
          exp = expBoolOU();                    // exp prend pour valeur le résultat retourné par la fonction expBoolOU()
          sauterSymCour(")");                   // on saute )

          seqinst = seqInst();                  // seqinst prend pour valeur le résultat retourné par la fonction seqInst()
          suivant = instSi();                   // suivant prend pour valeur le résultat retourné par la fonction instSi()
          sauterSymCour("finsi");               // on saute finsi
        
          si = new NoeudSi(exp,seqinst,suivant);  // si pointe sur un nouveau noeud de type NoeudSi (appel au constructeur NoeudSi avec les parametres exp,seqinst et suivant)
        }
                                
       else if (ls.getSymCour()==("sinonsi")) {   // sinonsi le symbole courant est sinonsi
          sauterSymCour("sinonsi");               //
          sauterSymCour("(");                     //
          exp = expBoolOU();                      //
          sauterSymCour(")");                     //
          seqinst = seqInst();                    //
          suivant = instSi();                     //
          si = new NoeudSi(exp,seqinst,suivant);  // si pointe sur un nouveau noeud de type NoeudSi (appel au constructeur NoeudSi avec les parametres exp,seqinst et suivant)
        }
                
        else if (ls.getSymCour()=="sinon") {      // sinonsi le symbole courant est sinon
           sauterSymCour("sinon");
           seqinst = seqInst();
           exp = ts.chercheAjoute(Symbole("1"));  // exp a pour valeur le pointeur du symbole valué -1 renvoyé par la methode chercheAjoute
                                                  // N.B: si -1 n'existait pas, la methode chercheAjoute insère le nouveau symbole valué -1
           si = new NoeudSi(exp,seqinst,suivant);    
        }
        return si;
}

        //------------------------------- SWITCH -------------------------------

Noeud* LecteurPhraseAvecArbre::instSwitch() {
    //<instSelon> ::= selon <variable> {cas <expression> : <seqInst>} [ defaut : <seqInst>]
    
    NoeudSwitch* switch = new NoeudSwitch();
    sauterSymCour("switch");
    testerSymCour("<VARIABLE>");
    switch->ajouterVariable(ts.chercheAjoute(ls.getSymCour()));
    ls.suivant();
    while (ls.getSymCour() == "cas") {
        sauterSymCour("cas");
        selon->ajouterExpression(expression());
        sauterSymCour(":");
        selon->ajouterSeqInst(seqInst());
    }
    if (ls.getSymCour() == "defaut") {
        sauterSymCour("defaut");
        sauterSymCour(":");
        selon->ajouterSeqInst(seqInst());
    }
    sauterSymCour("finselon");
    return selon;
}

        //------------------------------ TANT QUE ------------------------------

Noeud* LecteurPhraseAvecArbre::instTq() {       // <instTq> ::= tantque ( <expBool> ) <seqInst> fintantque

        sauterSymCour("tantque");
        sauterSymCour("(");
        Noeud* exp = expBoolOU();
        sauterSymCour(")");
        Noeud* seqinst = seqInst();
        sauterSymCour("fintantque");
        return new NoeudTantQue(exp,seqinst);
}

        //-------------------------------- POUR --------------------------------

Noeud* LecteurPhraseAvecArbre::instPour() {      // <instPour> ::= pour ( affectation ; <expBool>; affectation ) <seqInst> finpour

        sauterSymCour("pour");
        sauterSymCour("(");
        Noeud* aff = affectation();
        sauterSymCour(";");
        Noeud* exp = expBoolOU();
        sauterSymCour(";");
        Noeud* aff2 = affectation();
        sauterSymCour(")");
        Noeud* seqinst = seqInst();
        sauterSymCour("finpour");
        return new NoeudPour(aff,aff2,exp,seqinst);
}

        //------------------------------ REPETER -------------------------------

Noeud* LecteurPhraseAvecArbre::instRepeter() {   // <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )

        sauterSymCour("repeter");
        Noeud* seqinst = seqInst();
        sauterSymCour("jusqua");
        sauterSymCour("(");
        Noeud* exp = expBoolOU();
        sauterSymCour(")");
        return new NoeudRepeter(exp,seqinst);
}

        //------------------------------- LIRE ---------------------------------

Noeud* LecteurPhraseAvecArbre::instLire() {      //<instLire> ::= lire ( <variable> )

        Noeud* fact = NULL;
        sauterSymCour("lire");
        sauterSymCour("(");
        
        if (ls.getSymCour()=="<VARIABLE>") {
                fact = facteur();
                sauterSymCour(")");
        }
        else
                erreur("<variable>");
        return new NoeudLire(fact);
}

        //------------------------------ ECRIRE --------------------------------

Noeud* LecteurPhraseAvecArbre::instEcrire() {    //   <instEcrire> ::= ecrire ( <expression> | <chaine> )
       
        Noeud* exp = NULL;
        sauterSymCour("ecrire");
        sauterSymCour("(");
        
        if (ls.getSymCour()=="<CHAINE>") {
                testerSymCour("<CHAINE>");
                exp = ts.chercheAjoute(ls.getSymCour());
                ls.suivant();
        }
        else
           exp = expression();
        
        sauterSymCour(")");
        return new NoeudEcrire(exp);
   }

Noeud* LecteurPhraseAvecArbre::instSwitch() {
    //<instSwitch> ::= switch ( <variable> ) { cas ( <entier> ) <seqInst> break } [ defaut <seqInst> break]
    
}

        //---------------------------- AFFECTATION -----------------------------

Noeud* LecteurPhraseAvecArbre::affectation() {  // <affectation> ::= <variable> = <expression>

	testerSymCour("<VARIABLE>");
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
	ls.suivant();
	sauterSymCour("=");
	Noeud* exp = expression();
	return new NoeudAffectation(var,exp);
}

        //---------------------------- RELATION -----------------------------

Noeud* LecteurPhraseAvecArbre::relation() {    // <relation> ::= <expression> {<opRel> <expression>}
                                                // <expression> { <opRel> <expression> }

        Noeud* exp = expression();              //  expession prend pour valeur celle retournée par la méthode expression
        
        while ( ls.getSymCour()=="==" ||        // tant que le symbole lu est un '=='
                ls.getSymCour()=="!=" ||        // ou '!='
                ls.getSymCour()=="<=" ||        // ou '<'
                ls.getSymCour()=="<"  ||        // ou '<='
                ls.getSymCour()==">=" ||        // ou '>'
                ls.getSymCour()==">") {         // ou '>='
                Symbole operateur = opRel();    
                Noeud* expDroit = expression(); // expession de droite a pour valeur celle retournée par la méthode expression
                exp = new NoeudOperateurBinaire(operateur, exp, expDroit); // const. du noeud
        }
        return exp;
}

        //---------------------------- EXPRESSION ------------------------------

        Noeud* LecteurPhraseAvecArbre::expression() {   // <expression> ::= <terme> { <opAdd> <terme> }
            
                Noeud* term = terme();
        while (ls.getSymCour()=="+" || ls.getSymCour()=="-") {
                Symbole operateur =opAdd();
                Noeud* termDroit = terme();
                term = new NoeudOperateurBinaire(operateur,term,termDroit); // const. du noeud
        }
        return term;
}

        //---------------------------- TERME ------------------------------

        Noeud* LecteurPhraseAvecArbre::terme() {        // <terme> ::= <facteur> { <opBinaire> <facteur> }

                Noeud* fact = facteur();
        while (ls.getSymCour()=="*" || ls.getSymCour()=="/") {
                //opMult();
                //facteur();
                Symbole operateur =opMult(); // on stocke le symbole de l'opérateur
                Noeud* factDroit=facteur(); // lecture de l'operande droit
                fact = new NoeudOperateurBinaire(operateur,fact,factDroit); // const. du noeud
        }
        return fact;
}

        //---------------------------- FACTEUR ------------------------------

        Noeud* LecteurPhraseAvecArbre::facteur() {      // <facteur>    ::= <entier>  |  <variable>  |  <opUnaire> <expBool>  |  ( <expBool> )

	Noeud* fact=NULL;

	if (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="<ENTIER>") {
		fact=ts.chercheAjoute(ls.getSymCour());
		ls.suivant();
	} else if (ls.getSymCour()=="-") {
		ls.suivant();
		// on représente le - unaire (type: -facteur) par une soustractin binaire (0 - facteur)
		fact = new NoeudOperateurBinaire(Symbole("-"), ts.chercheAjoute(Symbole("0")), facteur());
	} else if (ls.getSymCour()=="non") {
                ls.suivant();
                fact= ts.chercheAjoute(Symbole("1"));
                // on représente le non par une valeur NULL a droite.
                fact = new NoeudOperateurBinaire(Symbole("!"), fact, facteur());
                                
        } else if (ls.getSymCour()=="(") {
		ls.suivant();
		fact=expBoolOU();
		sauterSymCour(")");
	} else
		erreur("<facteur>");
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
//--------------------- Expressions et opérateurs booleens --------------------- 
////////////////////////////////////////////////////////////////////////////////


        //------------------------------- Exp ET -------------------------------

        Noeud* LecteurPhraseAvecArbre::expBoolET() {            // <expBoolET>    ::= <relation> { <opBoolET> <relation> }
    
            Noeud* rel = relation();
        while (ls.getSymCour()=="et") {                 // si symbole courant est 'et'
            Symbole operateur = opBoolET();             // 
            Noeud* relDroit = relation();               // appel de la méthode relation pour tester l'élément suivant
            rel = new NoeudOperateurBinaire(operateur,rel,relDroit);
        }
        return rel;
}    
   

        //------------------------------- Exp OU -------------------------------

        Noeud* LecteurPhraseAvecArbre::expBoolOU() {            // <expBoolOU>    ::= <expBoolET> { <opBoolOU> <expBoolET> }
    
            Noeud* rel = expBoolET();
        while (ls.getSymCour()=="ou") {                // si symbole courant est 'ou'
		Symbole operateur = opBoolOU();         // appel de la méthode opBoolOU() pour continuer l'analyse
		Noeud* relDroit = expBoolET();          // appel de la méthode expBoolET
                rel = new NoeudOperateurBinaire(operateur,rel,relDroit);
        }
        return rel;
}

        //------------------------------- Op ET --------------------------------

        Symbole LecteurPhraseAvecArbre::opBoolET(){     // <opBool> ::= et
            Symbole operateur;
        if (ls.getSymCour()=="et" ){                    // si le symbole courant est un 'et'
            operateur=ls.getSymCour();                  // l'opérateur a pour valeur "et"
            ls.suivant();                               // on passe au suivant
        }
        else                                            // sinon
            erreur("<opBoolET>");                       // on appelle la methode erreur avec le message "<opBool>"       
            
    return operateur;
}  

        //------------------------------- Op OU --------------------------------

        Symbole LecteurPhraseAvecArbre::opBoolOU(){          // <opBool> ::= ou

            Symbole operateur;
        if (ls.getSymCour()=="ou" ) {           // si le symbole courant est un 'ou'
            operateur=ls.getSymCour();          // l'opérateur a pour valeur "ou"
            ls.suivant();                       // on passe au suivant
        }                 
        else                                    // sinon
            erreur("<opBoolOU>");               // on appelle la methode erreur avec le message "<opBool>"
        return operateur;
}
      
  //------------------------------------------------------------------------\\   
 //-------------------------------- OPERATEURS ------------------------------\\   
//----------------------------------------------------------------------------\\

        //-------------------------------- REL ---------------------------------

        Symbole LecteurPhraseAvecArbre::opRel() {            // <opRel> ::= == | != | < | <= | > | >=
    
            Symbole operateur;
        if (ls.getSymCour()=="==" ||ls.getSymCour()=="!=" ||
            ls.getSymCour()=="<=" ||ls.getSymCour()=="<"  ||
            ls.getSymCour()==">=" ||ls.getSymCour()==">"  ) {
            operateur=ls.getSymCour();
            ls.suivant();
        }
        else
                erreur("<opRel>");
        return operateur;
}

        //------------------------------- UNAIRE -------------------------------

        Symbole LecteurPhraseAvecArbre::opUnaire(){          // <opUnaire>   ::= - | non    
 
            Symbole operateur;
        if (ls.getSymCour()=="-" || ls.getSymCour()=="non"){
            operateur=ls.getSymCour();
            ls.suivant();
        }
        else
                erreur("<opUnaire>");
        
        return operateur;
}
        
        //--------------------------------- AD ---------------------------------

        Symbole LecteurPhraseAvecArbre::opAdd() {       // <opAdd> ::= + | -
            
	   Symbole operateur;
	if (ls.getSymCour()=="+" || ls.getSymCour()=="-" ) {
            operateur=ls.getSymCour();
            ls.suivant();
	}
	else
		erreur("<opAdd>");
	return operateur;
}
        //-------------------------------- MULT --------------------------------

        Symbole LecteurPhraseAvecArbre::opMult() {      // <opMult> ::= *  | /
	
        try{    
            Symbole operateur;
	if (ls.getSymCour()=="*" || ls.getSymCour()=="/" ) {
            operateur=ls.getSymCour();
            ls.suivant();
	}
	else
            erreur("<opMult>");
	return operateur;
        }catch (ExDivZero e) {throw e;} 
}

////////////////// OUTILS POUR SIMPLIFIER L'ANALYSE SYNTAXIQUE \\\\\\\\\\\\\\\\\


void LecteurPhraseAvecArbre::testerSymCour(string ch) {
	if (ls.getSymCour() != ch) {
		cout << endl << "-------- Erreur ligne " << ls.getLigne()
				<< " - Colonne " << ls.getColonne() << endl << "   Attendu : "
				<< ch << endl << "   Trouve  : " << ls.getSymCour() << endl
				<< endl;
		exit(0); // plus tard, on levera une exception
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
	testerSymCour(ch);
	ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreur(string mess) {
	cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
			<< ls.getColonne() << endl << "   Attendu : " << mess << endl
			<< "   Trouve  : " << ls.getSymCour() << endl << endl;
	exit(0); // plus tard, on levera une exception
}